1. Field of the Invention
The present invention relates to software development tools, particularly compilers, and to the Java programming language.
2. State of the Art
An embedded system is a specialized computer system that is part of a larger system or machine. Typically, an embedded system is housed on a single microprocessor board with the programs stored in ROM. Virtually all appliances that have a digital interface--watches, microwaves, VCRs, cars--utilize embedded systems. Some embedded systems include an operating system, but many are so specialized that the entire logic can be implemented as a single program.
Although software development for general-purpose computers and software development for embedded systems have much in common, software development for embedded systems is driven by substantially different needs and considerations. In particular, software for embedded systems is typically required to be as fast and compact as possible. Small code size reduces device cost and may contribute also to speed of execution.
A significant impediment to embedded systems development has been the vast variety of embedded system platforms. Unlike the general-purpose computer arena in which a few processors command most of the market, in the embedded systems arena, hundreds of different processors compete for market share. Porting embedded systems software from one processor to another has therefore been required in order to achieve software reuse. Such porting can only be performed by skilled programmers and is laborious, time-consuming, and error-prone.
For general-purpose computers, the Java programming language has emerged, holding the promise of "write-once, run anywhere," a promise which has been at least partly realized. To achieve the same progress in the embedded systems arena would represent a significant advance. However, several obstacles prevent the ready application of Java to embedded systems.
Referring to FIG. 1, the traditional Java implementation model is to use a Java compiler 101 (e.g, javac) to compile Java source code 103 into .class files 105 containing machine-independent byte-code instructions. These .class files are downloaded and interpreted by a browser or some other Java "Virtual Machine" 107 (VM). System services may be offered in the form of class libraries, or .so files 109.
The Java VM is conceptually a stack machine. The instructions interpreted by the VM manipulate data stored within "stack slots." At any given instant, the data within a stack slot may be of any of a number of data types defined in the Java language specification. A Java verifier ensures that type constraints are not violated, e.g., that a stack slot containing an integer is not added to a stack slot containing a string.
A run-time model of the Java VM is shown in FIG. 2. When a method is called, a frame is pushed onto a frame stack 200. In FIG. 2, two frames are shown, a first frame 201 corresponding to a first Method A and a second frame 203 corresponding to a second Method B. A frame pointer FP points to the current frame. A frame includes, for example, a pointer 205 to the frame of the caller, a pointer 207 to a method descriptor corresponding to the called method, a program counter (PC) 209, a stack pointer (SP) 211, a local registers area 213, and an expression stack 215. Each method has a corresponding method descriptor, shown in FIG. 2 as method descriptors 217 and 219, respectively. The size of the local registers area and the expression stack within a frame are determined by parameters max.sub.-- vars (221) and max.sub.-- stack (223), respectively, within the corresponding method descriptor. The method descriptor also contains a pointer 225 to the method code. Assume, for example, that Method A calls Method B. Within the code 227 of Method A, there will be instructions to push arguments onto the expression stack for use by Method B. These arguments are outgoing arguments from the standpoint of Method A. After the arguments have been pushed, the following code instructions will invoke Method B, which causes a frame for Method B to be pushed onto the frame stack, outgoing arguments from Method A to be copied as incoming arguments to the first part of the local registers area of the frame of Method B, and finally code 229 of Method B to be executed.
FIG. 3 represents the functional relationships between various entities of a machine running Java in accordance with the traditional model. Byte code 301, obtained using a source to byte code compiler 303, runs on top of a Java VM 305. The Java VM may take the form of an interpreter or a "Just-in-Time" (JIT) compiler. Class libraries 307 provide services to the Java VM and to the byte code program(s), in particular basic language support and extended functionality. A run-time library 309 provides low-level garbage collection (GC) and threads support and runs directly on top of the host platform operation system 311, which in turn runs on the machine hardware, or silicon 313.
The foregoing Java technology model enjoys considerable support. However, interpreting bytecodes make Java programs many times slower than comparable C or C++programs. One approach to improving this situation is JIT compilers. These dynamically translate bytecodes to machine code just before a method is first executed. This can provided substantial speed-up, but it is still slower than C or C++. There are three main drawbacks with the JIT approach compared to conventional compilers. First, the compilation must be re-done every time the application is loaded, which means start-up times are much worse than pre-compiled code. Second, since the JIT compiler has to run fast (it is run every time the application is run), it cannot do any non-trivial optimization. Only simple register allocation and "peep-optimizations" are practical. The need for quick re-compilation will make it very difficult to make JIT faster in practice. Third, the JIT compiler must remain in virtual memory while the application is executing. This memory may be quite costly in an embedded application.
Also during JIT compilation, before code is executed, considerable time may be spent initializing structures and tables. A Java class file includes substantial "metadata" describing the class. This metadata cannot be efficiently accessed from the class file itself. Instead, the Java run-time environment must read the metadata and from that metadata build an extensive data structure that describes each class with its fields and methods. For example, referring to FIG. 4, assume a class "IntList" that implements a linked list. (The source code in FIG. 10, described hereinafter, assumes the same example class.) In memory, an object reference (pointer) 401 points to a first list object 410 having a pointer 403 to a list class descriptor 430, a value field 405 (containing the value 10), and a pointer 407 to a next list object 420. The next list object 420 also points to the list class descriptor 430 and contains a value 415 (20). The pointer 417 to the next list object is null. The list class descriptor 430 contains pointers to other objects, for example a pointer 421 to an object 440 describing the fields of a list object. The object 440 contains the names, types and byte offsets of the fields "value" and "next." In addition, a Class Table 450 lists the various loaded class and points to their class descriptors. Building the foregoing data structure increases memory requirements and prolongs start-up time.
A degree of machine independence is already achieved by an embedded systems development environment of the present assignee based on the GNU C compiler (gcc). In this development environment, machine-specific compiler "back-ends" for more than 100 embedded systems processors interface to a common gcc compiler "front-end," enabling code written in C/C++to be compiled for any of the supported processors. For write-once run anywhere to span both general-purpose computers and embedded systems, however, and for embedded systems to become Web-enabled, a Java-based embedded-systems development solution is required.